/***************************************************************************
                  context.h  -  description
                    -------------------
   begin           : Fri Jun 21 2002
   copyright        : (C) 2003 by Michiel Visser
   email           :
 ***************************************************************************/

/***************************************************************************
 *                                                 *
 *  This program is free software; you can redistribute it and/or modify  *
 *  it under the terms of the GNU General Public License as published by  *
 *  the Free Software Foundation; either version 2 of the License, or    *
 *  (at your option) any later version.                        *
 *                                                 *
 ***************************************************************************/

#ifndef CONTEXT_H
#define CONTEXT_H

#include "mstl.h"
#include "object.h"
#include <map>
#include <vector>
#ifndef __WIN__
#include <semaphore.h>
#include <time.h>
#include <pthread.h>
#endif

using namespace Obj;
using namespace Expression;
using namespace std;


namespace Sess
{

struct Painter
{
	Painter *m_next;
};

template<typename Mem, int I>struct PainterSub: public Painter
{
	Mem m_obj;
	inline PainterSub(Mem obj);
	inline ~PainterSub();
	void set(Mem o) { m_obj=o;}
};

struct SelfPainter: public PainterSub<const Object *,0>
{
	SelfPainter(): PainterSub<const Object *,0>(0) { }
	SelfPainter(const Object *o): PainterSub<const Object *,0>(o) {}
};

struct ChildrenPainter: public PainterSub<const Object *,1>
{
	ChildrenPainter(): PainterSub<const Object *,1>(0) {}
	ChildrenPainter(const Object *o): PainterSub<const Object *,1>(o) { }
};

struct OListChildrenPainter: public PainterSub<const OList &,2>
{
	OListChildrenPainter(const OList &list): PainterSub<const OList &,2>(list) {}
};

}

namespace Obj
{
class ObjectHolderCP: public InheritanceHolder<Object,OBJ_SIZE>
{
private:
   ChildrenPainter m_cp;
public:
   void protectChilds() { m_cp.set((Object *)*this); }
   ObjectHolderCP() {}
   inline ~ObjectHolderCP() { _ASSERT(m_cp.m_obj!=0); }
};
}

namespace Sess
{

struct StructIndex
{
	String m_name;
	int m_index; // must be updated
	int update();
	StructIndex(String name):m_name(name), m_index(0) {}
};

class StructElem
{
public:
	//typedef Ptr<StructElem>::Share Ptr;
	String m_name;
	//int m_index; todo ! necessary?
	TypeVarPtr m_typeVarPtr;
	StructElem(String name,  TypeVar t);
//	bool operator==(const String &c) { return name==c; }
};

bool operator==(const String &c, const StructElem &s);

struct TypeFunc;

Object *bracketize(Object *o);

class TypeVal
{
	bool m_isDynamic;
	bool m_isSystem;
public:
	TypeVar m_childType;
	Vector <StructElem> m_elems;
	String m_name;
	TypeVal(String name=String() ): m_isDynamic(false), m_isSystem(false), m_elems(0), m_name(name) {}
	TypeVal( TypeVar key, TypeVar childType);
	TypeVal(const TypeVal &left,const TypeVal &right);
	TypeVal(TypeVar childType): m_isDynamic(false), m_isSystem(false), m_childType(childType) {}
	void set(const List<StructElem> &elems, bool isDynamic);
	void set(const TypeVal &from);
	static TypeVar getCopy(TypeVal *from);
	inline void setSystem() { m_isSystem = true; }
	inline bool isSystem() const { return m_isSystem; }
	inline bool isMap() const { return m_elems.length() && m_elems[0].m_name[0]==0; }
	inline bool isAnonymous() const { return m_name.length()==0 ;}
  /*void setArrayChild(TypeVar s, TypeVar **sHolder)
	{ firstDynamicElem=-1;
	  m_childType=s;
		if (sHolder)
			*sHolder = &m_childType;
	}*/
	void getElemNames(OList &o);
	TypeVar getKey() const { return m_elems.length() ? *m_elems[0].m_typeVarPtr : TypeVar(); }
	TypeVar *getKeyPtr() const { return m_elems.length() ? (TypeVar *)(m_elems[0].m_typeVarPtr) : 0; }
	int findElem(String m);
//  void erase() { m_elems.erase(); end->m_index=0; }
 	bool isEmpty() { return !m_elems && !m_childType; }
	void elemsToArray(OList::P &array, List<TypeFunc>::P &funcs) const;
	Object *elemsToArrayObject(const List<TypeFunc> &funcs) const;
	Object *toArrayObject(int parentlevel, List<TypeFunc>::P &funcs) const;
	inline Object *toArrayObject(const List<TypeFunc> &funcs) const;
};

struct VarValue
{
	Object *m_value;
	TypeVarPtr m_typeVarPtr;
	VarValue(): m_value(NullObject::obj()) { new (m_typeVarPtr) TypeVar(); }//TODO: correct? old: new (*new (m_typeVarPtr) TypeVar()) TypeVal();
	//VarValue(Object *value, TypeVar type): m_value(value) {  new (m_typeVarPtr) TypeVar(type); }
	VarValue(Object *value, 	TypeVarPtr typePtr): m_value(value), m_typeVarPtr(typePtr) {}
};

class LocalVarVectorQueue;

class Variable
{	
public:
	String name;	
	EPtr expr;
	Variable(const String &n, EPtr e=EPtr()): name(n), expr(e) {}
public:
	friend bool operator==(const String &c, const Variable &v) { return v.name==c; }
};

struct Scope
{
	virtual EPtr findVar(String s, bool localFind) = 0;
	virtual EPtr insertVar(String s, bool localFind);
	virtual bool isEmpty() const;
	virtual void GCPaint();
	typedef Ptr<Scope>::Share::NoCopy Ptr;
	Ptr m_father;
	virtual ~Scope();
};

struct NamedIteratorVarsScope: public Scope
{
	int m_depth;
	String m_name;
	String m_keyName;
	EPtr findVar(String s, bool localFind);
	NamedIteratorVarsScope(String name, String keyName, int depth): m_depth(depth), m_name(name), m_keyName(keyName) {}
	~NamedIteratorVarsScope();
};

struct ParamListScope: public Scope
{
	TypeVar m_type;
	EPtr findVar(String s, bool localFind);
	ParamListScope(TypeVar type): m_type(type) {}
	~ParamListScope();
};

struct Closure: public Variable
{
	EPtr m_intern;
	Closure(const String &n, EPtr ext, EPtr intern): Variable(n,ext),m_intern(intern) {}
	bool operator==(const String &s) { return s==name; }
};

class ClosureScope: public Scope // aka FunctionScope
{
public:
	//unsigned short m_varCount;
	List<Closure> m_closures;
	int m_count;
	int m_depth;
	EPtr findVar(String s, bool localFind);
	ClosureScope(int depth): m_count(0), m_depth(depth) {}
	~ClosureScope();
};

struct ClosureBinding
{
	EPtr m_expr;
	ClosureScope::Ptr m_scope;
	Vector<VarValue> m_vars;
	ClosureBinding(EPtr to, ClosureScope::Ptr scope);
	ClosureBinding(ClosureScope::Ptr scope) : m_scope(scope) {}
	~ClosureBinding() {}
//	ClosureBinding(const ClosureBinding &b): m_expr(b.m_expr), m_scope(b.m_scope), m_vars(b.m_vars) {}
//  ClosureBinding(const ClosureBinding *b);
};

class LocalVarVectorQueue: public Scope
{
	List< Vector<VarValue> > m_stack;
	unsigned short m_varCount;
	List<Variable> vars;
public:
	//typedef Ptr<LocalVarVectorQueue>::Share::NoCopy Ptr;
	LocalVarVectorQueue() : m_varCount(0) {}
	EPtr findVar(String s, bool localFind);
	EPtr insertVar(String s, bool localFind);
	int updateCount() { return m_varCount=vars.length(); }
	Object * &operator[](int i) { return (*m_stack)[i].m_value; }
	VarValue &getVarValue(int i) { return (*m_stack)[i]; }
	void GCPaint();
	void push();
	String getName(int i) const { return vars[i].name; }
	//TypeVar &getStruct(int i) { return (*m_stack)[i].m_strct; }
	bool isEmpty() const;
	void pop();
	void erase() { vars.erase(); }
	~LocalVarVectorQueue();
protected:
	Ptr m_father;
};

extern TypeVar RegExpResultType;

class GlobalVarVector;
class GlobalVariable: public Variable
{
public:
	Object *m_value;
	TypeVar m_strct;
	GlobalVariable(const String &n, Object *v):Variable(n), m_value(v) {}
  String getName() const { return name; }
  TypeVar &getType() { return m_strct; }
};

struct NameSet
{
	Object::HolderCP m_names;
	//void dumpVars();
	NameSet() { new (m_names) MapObject(); m_names.protectChilds(); }
	void insertName(String name) {	m_names->insertAt( NEW_OBJ StringObject(name), NullObject::obj() ); }
};


class GlobalVarVector: public NameSet
{
	Hash<GlobalVariable,4096> hash;
public:
//	ObjectHolderCP m_names;
	//void dumpVars();
	GlobalVarVector() {}
  EPtr findVar(String s);
  EPtr getVarExpression(String s);
  GlobalVariable *registerVar(String name, Object *v=NullObject::obj()) ;
  inline GlobalVariable *getVar(String name) 
  {	GlobalVariable *sv=hash.find(name);
  	return sv ? sv : registerVar(name);
	}
  void insertVar(String s, Object * r);
  void GCPaint();
	void erase();
};

void fileLogging(bool fl);
void print(char *format, ...);
#define LINELOG if (s_fl) Sess::print("%s %i\n",__FILE__,__LINE__);

bool operator==(const String &c, const TypeVar &s);

struct TypeFunc
{ 
	TypeFunc() {}
  virtual TypeVar evaluate(bool &once)=0;
  TypeVar evaluate() { bool once; return evaluate(once); }
	virtual ~TypeFunc();
	virtual bool assignsToTypeIdentifier(const TypeVal *type) const;
	virtual bool assignsToStructElement(TypeVar parent, int elem) const;
	virtual Object *toArrayObject(int parentlevel, List<TypeFunc>::P &funcs) const;
	Object *toArrayObject(const List<TypeFunc> &funcs) const  { List<TypeFunc>::P pfs(&funcs); return toArrayObject(100,pfs); }
};

inline Object *TypeVal::toArrayObject(const List<TypeFunc> &funcs) const  { List<TypeFunc>::P pfs(&funcs); return toArrayObject(100,pfs); }

struct TypeAssignment: public TypeFunc
{ 
	Ptr<TypeFunc>::Own m_l, m_r;
  TypeAssignment(TypeFunc *l, TypeFunc *r): m_l(l), m_r(r) {}
  TypeVar evaluate(bool &once);
	~TypeAssignment();
	bool assignsToTypeIdentifier(const TypeVal *type) const;
	Object *toArrayObject(int parentlevel, List<TypeFunc>::P &funcs) const;
};

struct PreTypeSelection: public TypeFunc // must be same size as TypeSelection or bigger
{ String m_typeName;
  PreTypeSelection(String name): m_typeName(name) {}
  TypeVar evaluate(bool &once);
	~PreTypeSelection();
	bool assignsToTypeIdentifier(const TypeVal *type) const;
	Object *toArrayObject(int parentlevel, List<TypeFunc>::P &funcs) const;
};

struct TypeSelection: public TypeFunc
{ TypeVar m_type;
  TypeSelection(TypeVal *type): m_type(type) {}
  TypeVar evaluate(bool &once);
	~TypeSelection();
	bool assignsToTypeIdentifier(const TypeVal *type) const;
	Object *toArrayObject(int parentlevel, List<TypeFunc>::P &funcs) const;
};

/*struct TypeVarSelection: public TypeFunc
{ TypeVar m_typeVar;
  TypeVarSelection(TypeVar typeVar): m_typeVar(typeVar) {}
  TypeVar evaluate(bool &once);
};*/

struct StructIndexing: public TypeFunc
{ 
	Ptr<TypeFunc>::Own m_type;
	StructIndex m_index;
  StructIndexing(TypeFunc *type, String name): m_type(type), m_index(name) {}
  TypeVar evaluate(bool &once);
	~StructIndexing();
	Object *toArrayObject(int parentlevel, List<TypeFunc>::P &funcs) const;
};

struct StructConcat: public TypeFunc
{ 
	Ptr<TypeFunc>::Own m_l, m_r;
  StructConcat(TypeFunc *l, TypeFunc *r): m_l(l), m_r(r) {}
  TypeVar evaluate(bool &once);
	~StructConcat();
	Object *toArrayObject(int parentlevel, List<TypeFunc>::P &funcs) const;
};
struct TypeToStructElemAssignment: public TypeFunc
{
	TypeVar m_parent;
	int m_elem;
	TypeFunc *m_subFunc;
	TypeToStructElemAssignment(TypeVar parent, int elem, TypeFunc *subFunc): m_parent(parent), m_elem(elem), m_subFunc(subFunc) {}
  TypeVar evaluate(bool &once);
	~TypeToStructElemAssignment();
	bool assignsToStructElement(TypeVar parent, int elem) const;
	Object *toArrayObject(int parentlevel, List<TypeFunc>::P &funcs) const;
};

#ifdef __WIN__
class Semaphore
{  // states: released, claimed
  HANDLE m_sem;
public:
  Semaphore(bool startClaimed=false) { m_sem=CreateSemaphore(NULL,startClaimed?0:1,1,NULL); }
  ~Semaphore() { CloseHandle(m_sem); }
  bool claim(unsigned int timeout=INFINITE) { return WaitForSingleObject(m_sem, timeout)!=WAIT_OBJECT_0; }
  void release() { ReleaseSemaphore(m_sem,1,NULL);}//LeaveCriticalSection (&m_cs);}
};

class CriticalSection
{ CRITICAL_SECTION m_cs;
public:
  CriticalSection() { InitializeCriticalSection(&m_cs); }
  ~CriticalSection() { DeleteCriticalSection (&m_cs); }
  void enter() { EnterCriticalSection (&m_cs); }
  void leave() { LeaveCriticalSection (&m_cs);}
};

#else
class Semaphore
{  // states: released, claimed
  sem_t m_sem;
public:
  Semaphore(bool startClaimed=false) { sem_init(&m_sem, 0, startClaimed?0:1); }
  ~Semaphore() { sem_destroy(&m_sem); }
  void claim() { sem_wait(&m_sem); }
  bool claim(unsigned int timeoutMs) {
	  struct timespec t;
	  if (clock_gettime(CLOCK_REALTIME, &t) == -1)
	         return true;
	  t.tv_nsec+=timeoutMs*1000000;
	  t.tv_sec+=t.tv_nsec/1000000000;
	  t.tv_nsec%=1000000000;
	  return sem_timedwait(&m_sem, &t)==0;
  }
  void release() { sem_post(&m_sem);}//LeaveCriticalSection (&m_cs);}
};

class CriticalSection
{
	pthread_mutex_t m_cs;
public:
	CriticalSection() { pthread_mutex_init(&m_cs,0); }
	~CriticalSection() { pthread_mutex_destroy(&m_cs); }
	void enter() { pthread_mutex_lock(&m_cs); }
	void leave() { pthread_mutex_unlock(&m_cs);}
};
#endif

class Thread;

struct StackVar
{ Object *val;
  TypeVarPtr m_typePtr;
  StackVar(Object *v=0, TypeVar s=TypeVar()): val(v) { new (m_typePtr) TypeVar(s); }
	inline void set(Object *o) { val=o; } //todo remove
};

extern Session session;

struct Painter;

class ThreadVars
{
protected:
  Scope::Ptr m_curScope;
  List<Scope::Ptr> m_execStack;
  List<StackVar> m_stack; // $-variable
public:
  ClosureBinding *m_curClosures;
  TypeVar *m_curTypePtr;
	Painter *m_firstPainters[3];
	void clearFirstPainters() {  m_firstPainters[0]=m_firstPainters[1]=m_firstPainters[2]=0; }
	TypeVar getCurType() { return m_curTypePtr ? *m_curTypePtr : TypeVar(); }
	TypeVar getCurTypeArrayChild() { return !m_curTypePtr || !*m_curTypePtr ? TypeVar() : (*m_curTypePtr)->m_childType; }
	ThreadVars() { clearFirstPainters();}
  void GCPaint();
};

class ExternObject: public Object::Holder
{
public:
	typedef List<ExternObject> MyList;
	MyList *m_holder;
	void remove()
	{	ExternObject *pn=m_holder->pnext();
		if (pn) pn->m_holder = m_holder;
		m_holder->removeone();
	}
}; 
#ifdef DEBUGLOGGING
typedef	struct { char *type; String name; } LogEntry;
#endif

struct PointerMapValue
{
	int m_index;
	int m_count;
	PointerMapValue(): m_index(-1), m_count(0) {}
	bool isInited() { return m_index!=-1; }
	void init() { m_index=1; }
/*	static bool operator<(const PointerMapValue &a, PointerMapValue &b) {
		return a.x<b.x;
	}*/
};

class Session: public ThreadVars
{
	friend class ClosureScope;
private:
	GlobalVarVector m_global;
	StackVar m_firstStackVar; // first dollar object
	void addFunc(EPtr e);
	List<TypeVar> m_types;
	CriticalSection m_execCs;
	typedef List<Ptr<Thread> > ThreadList;
public:
	enum FlowState { flow_run=0, flow_continueOrReturn, flow_break, flow_return, flow_exception };
	struct
	{
		Object::HolderNotDeleting result;
		bool hasResult;
		FlowState state;
	} m_flow;
	typedef map<const Object *, PointerMapValue> PointerMap;
	PointerMap m_pointerMap;
	void fillPointerMap(const Object *root);
	void clearPointerMap();
	Vector<Object *> m_sharedLiteralObjects; // todo: replace with std:vector
	const vector<EPtr> *m_curSharedLiterals;
	int m_pointerMapNextIndex;
	NameSet m_typeNames;
	bool m_annotate;
	int m_funcLevel;
	List<const Object *> m_childrenPaintStack;
	ExternObject::MyList m_externObjects;
	CriticalSection m_evalCs;
	ThreadList m_threads;
	Thread *m_curThread, *m_mainThread;
	EPtr m_exprNull, m_exprEmptyList, m_exprEmptyMap, m_exprNumbers[4], 
		m_argumentListExprs[4], m_iteratorVarExpr, m_iteratorKeyVarExpr, m_argumentExprs[4][2];
	typedef Ptr<Session>::Share MyPtr;
	Session(): m_annotate(false)
#ifdef DEBUGLOGGING
		, m_logQueue(0) 
#endif
	{}
	void GCPaint();
	inline void GCPaintObject(const Object *o) const {
		if (objectGC.paint(o))
			session.m_childrenPaintStack.prepend(o);
	}
	inline void GCPaintObjectPerm(const Object *o, int value) const { 
		objectGC.paintPermanent(o,value);
		o->GCPaintMyChildrenPerm(value);
	}
	void init();
	void quit();
	void enter();
	inline GlobalVariable *getGlobalVar(String s) { return m_global.getVar(s); }
	inline void setCurType(TypeVar t) { if (m_curTypePtr && (!*m_curTypePtr || !(*session.m_curTypePtr)->isSystem() ) ) *m_curTypePtr = t; }
	inline void enterLite() { m_execCs.enter(); } //enter without thread context
	inline void enterFromAnyThread();
	inline void leave()      { m_execCs.leave(); }
	void schedule();
	inline void selectGlobal() { m_curScope=0; }
	EPtr findOrInsert(String s, bool localFind);
#ifdef TYLE_V2
	int getDollarMultiplicity(int level);
#endif
	TypeVar findType(const String &n)
	{ TypeVar *p=m_types.finddata(n);
		return p?*p:TypeVar();
	}
	inline TypeVar newTypeEntry(String name) // only non-anonymous should be inserted
	{	TypeVar p;
		m_typeNames.insertName(name);
		new (p) TypeVal(name);
		m_types.prepend(p);
		return p;
	}
	ExternObject *evaluateToCopy(const unsigned char *pos);
	void reportRuntimeError(String str);
	void stackString(const char *str, int len);
	void execPush(Scope::Ptr q)
	{	m_execStack.prepend(m_curScope);
		m_curScope = q;
	}
	void execPop()
	{	List<Scope::Ptr> l;
		l.takeone(m_execStack);
		m_curScope=*l;
	}
	void execPushLvvq(LocalVarVectorQueue::Ptr q);//push the variableblock for execution
	void execPopLvvq();
	inline void pushStack(Object * o, TypeVar type=TypeVar())   { new (m_stack) StackVar(o, type); }
	inline void popStack()           {  m_stack.removeone(); }
	inline StackVar &getStackVar(int depth) { StackVar *p=m_stack.pnext(depth); return p?*p:m_firstStackVar; }
	inline void push(Scope::Ptr q) { q->m_father=m_curScope; m_curScope=q; } // push for scope
	inline void pop()
	{ m_curScope = m_curScope->m_father;
	}
	inline bool lvvqPop() {
		bool res = ((LocalVarVectorQueue *)&*m_curScope)->updateCount() != 0; // length of first elements (which is an list of strings)
		pop();
		return res;
	}
	Object *getGlobalNamesObject() { return m_global.m_names; }
	ExternObject *attachFirstExtern(ExternObject::MyList &from);
#ifdef DEBUGLOGGING
	VectorQueue< EPtr > m_logQueue;
	inline void setLogSize(int i) { m_logQueue.~VectorQueue(); new (&m_logQueue) VectorQueue< LogEntry >(i); }
	inline void log(Expr *expr) 
	{	if (m_logQueue.length())
			doLog(expr);
	}
	void doLog(Expr *expr);
	void getLog(void *space);
#endif
};

class Thread: public ThreadVars
{
	bool m_finite, m_onIncoming;
	int m_timeout;
	friend class Session;
#ifdef __WIN__
	HANDLE m_waitPipeRead, m_waitPipeWrite;
	DWORD m_id;
	short m_prio;
#else
	pthread_t m_thread;//, m_waitPipeRead, m_waitPipeWrite;
#endif
public:
	Thread *m_father;
	Object *m_func;
	Semaphore m_sem;
	List<Object> m_incoming;
	Thread();
	Thread(Thread *father, Object *func);
	//	void schedule(int timeout=0, bool onIncoming=false); 
	~Thread();
	void GCPaint();
#ifdef __WIN__
	inline friend bool operator==(const DWORD &id, const Ptr<Thread> &t) { return (t->m_id==id);  }
	void setVars(HANDLE me, DWORD threadId);
	static DWORD getCurId() {
		return GetCurrentThreadId();
	}
	void setToCurrent() { m_id = GetCurrentThreadId() ; }
  	bool isCurrent() { return m_id==getCurId(); }
#else
	inline friend bool operator==(pthread_t thread, const Ptr<Thread> &t) { return pthread_equal(t->m_thread, thread);  }
	bool isCurrent() { return pthread_equal ( m_thread, getCurId()); }
	static pthread_t getCurId() {
		return pthread_self();
	}
	void setToCurrent() { m_thread = getCurId() ; }
#endif
};

inline void Session::enterFromAnyThread()
{
	m_mainThread->setToCurrent();
	enter();
}

template<typename Mem, int I>	inline PainterSub<Mem,I>::PainterSub(Mem obj): m_obj(obj) { m_next=session.m_firstPainters[I]; session.m_firstPainters[I] = this; }
template<typename Mem, int I>	inline PainterSub<Mem,I>::~PainterSub() { if ( session.m_firstPainters[I] == this) session.m_firstPainters[I] = m_next; /*TODO: this should normally ASSERT, but the assert should not come on ctrl+c*/}

} // namespace sess

#endif // FILEXXX_H
